namespace Universe.RedisTests
{
    using System;
    using System.Diagnostics;
    using System.Globalization;
    using System.IO;
    using System.Threading;

    using WindowsRedisServer;

    using NUnit.Framework;

    using StackExchange.Redis;

    [TestFixture]
    public class AllTests
    {

        [Test]
        public void Test_2_Connect_To_Nonsense_Server()
        {
            var config = new ConfigurationOptions
            {
                AbortOnConnectFail = false,
                ResolveDns = false,
                EndPoints = { "127.0.0.1:1234" },
                ConnectTimeout = 200
            };
            var log = new StringWriter();
            using (var conn = ConnectionMultiplexer.Connect(config, log))
            {
                // Trace.WriteLine(log);
                Assert.IsFalse(conn.IsConnected);
            }

        }

        [Test]
        public void Test_1_Connect()
        {
            string hostAndPort = "127.0.0.1:" + RedisServerLauncher.TcpPort;
            var config = new ConfigurationOptions
            {
                AbortOnConnectFail = true,
                ResolveDns = false,
                EndPoints = { hostAndPort }, 
                ConnectTimeout = 9000
            };

            for (int i = 0; i <= 2; i++)
            {
                Stopwatch at = Stopwatch.StartNew();
                var log = new StringWriter();
                using (var conn = ConnectionMultiplexer.Connect(config, log))
                {
                    bool isOk = conn.IsConnected;
                    if (!isOk)
                    {
                        Trace.WriteLine(log);
                        Assert.Fail("Succesful Connection is expected to " + hostAndPort);
                    }
                }
                Trace.WriteLine("Next: " + at.Elapsed);
            }

        }

        [Test]
        public void Test_3_GetVersion()
        {
            using (ConnectionMultiplexer conn = NewConnection)
            {
                var server = conn.GetServer("127.0.0.1", RedisServerLauncher.TcpPort);
                if (server != null)
                {
                    Trace.WriteLine("Redis Server Version: " + server.Version);
                    Trace.WriteLine("Redis Server Version: " + server.Version);
                    Trace.WriteLine("Redis Server Version: " + server.Version);
                }
            }

        }

        [Test]
        public void Test_4_Simle_String()
        {
            using (ConnectionMultiplexer conn = NewConnection)
            {
                var db = conn.GetDatabase();
                for (int i = 0; i <= 2; i++)
                {
                    Stopwatch at = Stopwatch.StartNew();
                    var id = Guid.NewGuid().ToString();
                    var none = db.StringGet(id, CommandFlags.HighPriority);
                    db.KeyDelete(id);
                    var expected = "My Value";
                    db.StringSet(id, expected);
                    RedisValue actual = db.StringGet(id, CommandFlags.HighPriority);
                    Assert.IsTrue(actual.HasValue);
                    Assert.AreEqual(expected, (string) actual);
                    Console.WriteLine("Next: " + at.Elapsed);
                }
            }

        }

        [Test]
        public void Test_6_PubSub_From_Origin()
        {
            using (var mx = NewConnection)
            {
                Stopwatch at = Stopwatch.StartNew();
                var pub = mx.GetSubscriber();
                int a = 0, b = 0, c = 0, d = 0;
                pub.Subscribe(new RedisChannel("*bcd", RedisChannel.PatternMode.Literal), (x, y) =>
                {
                    Interlocked.Increment(ref a);
                    Trace.WriteLine("Message type A recieved by " + at.Elapsed);
                });
                pub.Subscribe(new RedisChannel("a*cd", RedisChannel.PatternMode.Pattern), (x, y) =>
                {
                    Interlocked.Increment(ref b);
                    Trace.WriteLine("Message type B recieved by " + at.Elapsed);
                });
                pub.Subscribe(new RedisChannel("ab*d", RedisChannel.PatternMode.Auto), (x, y) =>
                {
                    Interlocked.Increment(ref c);
                    Trace.WriteLine("Message type C recieved by " + at.Elapsed);
                });
                pub.Subscribe("abc*", (x, y) =>
                {
                    Interlocked.Increment(ref d);
                    Trace.WriteLine("Message type D recieved by " + at.Elapsed);
                });

                Thread.Sleep(1);
                at = Stopwatch.StartNew();
                pub.Publish("abcd", "efg");
                Thread.Sleep(500);
                Assert.AreEqual(0, Thread.VolatileRead(ref a), "a1");
                Assert.AreEqual(1, Thread.VolatileRead(ref b), "b1");
                Assert.AreEqual(1, Thread.VolatileRead(ref c), "c1");
                Assert.AreEqual(1, Thread.VolatileRead(ref d), "d1");

                pub.Publish("*bcd", "efg");
                Thread.Sleep(1500);
                Assert.AreEqual(1, Thread.VolatileRead(ref a), "a2");
                //Assert.AreEqual(1, Thread.VolatileRead(ref b), "b2");
                //Assert.AreEqual(1, Thread.VolatileRead(ref c), "c2");
                //Assert.AreEqual(1, Thread.VolatileRead(ref d), "d2");
            }
        }


        [Test]
        public void Test_5_Simple_PubSub()
        {
            Stopwatch at = Stopwatch.StartNew();
            int counter = 0;
            RedisChannel myChannel = "my-channel";
            using (ConnectionMultiplexer conn = NewConnection)
            {
                var su = conn.GetSubscriber();
                su.Subscribe(myChannel, (channel, value) =>
                {
                    var msg = (string) value;
                    Trace.WriteLine("Message recieved: [" + msg + "] by " + at.Elapsed);
                    Interlocked.Increment(ref counter);
                });
                Thread.Sleep(1);

                at = Stopwatch.StartNew();
                using (ConnectionMultiplexer conn2 = NewConnection)
                {
                    var su2 = conn2.GetSubscriber();
                    su2.Publish(myChannel, "Hello upthere!");
                    su2.Ping();
                }

                Thread.Sleep(1600);
                Assert.AreEqual(1, counter, "1 Message is expected");

            }
        }



        ConnectionMultiplexer NewConnection
        {
            get
            {
                string hostAndPort = "127.0.0.1:" + RedisServerLauncher.TcpPort;
                var config = new ConfigurationOptions
                {
                    AbortOnConnectFail = true,
                    ResolveDns = false,
                    EndPoints =
                    {
                        {hostAndPort}
                    },
                    ConnectTimeout = 9000
                };

                StringWriter log = new StringWriter();
                ConnectionMultiplexer conn = ConnectionMultiplexer.Connect(config, log);
                conn.ConnectionFailed += (sender, args) =>
                {
                    Trace.WriteLine("Connection failed [" + args.FailureType + "] " + Environment.NewLine +
                                    args.Exception + 
                                    Environment.NewLine
                                    + log);
                };

                bool isOk = conn.IsConnected;

                return conn;

            }

        }

        private static Stopwatch StartAt = Stopwatch.StartNew();
        [TestFixtureSetUp]
        public void TestFixtureSetUp()
        {
            CrossInfo.AttachUnitTrace("Redis Tests");
            RedisServerLauncher.Launch();
            StartAt = Stopwatch.StartNew();
        }

        Stopwatch TestAt = Stopwatch.StartNew();
        [SetUp]
        public void SetUp()
        {
            TestAt = Stopwatch.StartNew();
            Thread.CurrentThread.CurrentCulture = Thread.CurrentThread.CurrentUICulture = new CultureInfo("en-US");
            CrossInfo.NextTest();
        }

        [TearDown]
        public void TearDown()
        {
            // Trace.WriteLine(Environment.StackTrace + Environment.NewLine);
            if (CrossInfo.ThePlatform == CrossInfo.Platform.MacOSX || CrossInfo.ThePlatform == CrossInfo.Platform.Linux)
                Trace.WriteLine("***** " + TestAt.Elapsed + Environment.NewLine);

        }


        [TestFixtureTearDown]
        public void TestFixtureTearDown()
        {
            AppDomain.CurrentDomain.DomainUnload += (sender, args) =>
            {
            };

            Trace.WriteLine("Total time: " + StartAt.Elapsed);
        }

    }
}